from PyQt4.QtGui import * 
from PyQt4.QtCore import * 
from pixel import *

class ColorSwap(QWidget):
    def __init__(self, color, sprite_editor, parent = None):
        QWidget.__init__(self, parent)

        self.sprite_editor = sprite_editor

        self.color_a = ColorSwatch(50, 50, color)
        self.color_b = ColorSwatch(50, 50, color)

        self.vbox_main = QVBoxLayout()
        self.hbox_swatches = QHBoxLayout();
        self.vbox_main.addLayout(self.hbox_swatches)

        self.hbox_swatches.addWidget(self.color_a)
        self.hbox_swatches.addWidget(self.color_b)

        self.connect(self.color_b, SIGNAL("changed()"), self.go)

        self.setLayout(self.vbox_main)

    def go(self):
        result = self.sprite_editor.color_swap(self.color_a, self.color_b)
        if result:
            self.color_a.color = self.color_b.color
            self.color_a.update()

class ColorPickerDialog(QDialog):
    def __init__(self):
        QDialog.__init__(self, None)
        self.color_picker = ColorPicker()

        self.vbox = QVBoxLayout()
        self.vbox.setSizeConstraint(QLayout.SetMinAndMaxSize)
        self.vbox.setSpacing(0)
        self.vbox.addWidget(self.color_picker)

        self.setLayout(self.vbox)

class ColorPicker(QWidget):
    def __init__(self):
        QWidget.__init__(self, None)
        self.dragging_gradient = False
        self.dragging = False
        #self.setMouseTracking(True)
        self.h = 180

        
        #self.gradient_rect = QRect(0,0,255,255)
        self.hue_rect = QRect(0,0,359,16)
        self.sat_rect = QRect(0,self.hue_rect.bottom()+16,255,16)
        self.value_rect = QRect(0,self.sat_rect.bottom()+16,255,16)


        self.s = 255;
        self.h = 180;
        self.v = 255;

        self.selected_color = QColor.fromHsv(self.h,self.s,self.v)

        #self.initialize_spectrum()
        self.initialize_hue()
        self.initialize_saturation()
        self.initialize_value()

        self.setMinimumSize(360+self.value_rect.bottom(),self.value_rect.bottom())
        self.setMaximumSize(360+self.value_rect.bottom(),self.value_rect.bottom())
        self.resize(360+self.value_rect.bottom(),self.value_rect.bottom())

    def initialize_spectrum(self):
        self.spectrum_pixmap = QPixmap(self.gradient_rect.size())
        p = QPainter(self.spectrum_pixmap)

        for s in range(255):
            g = QLinearGradient(0,1,256,1)
            g.setColorAt(0.0,QColor.fromHsv(self.h,s,0))
            g.setColorAt(1.0,QColor.fromHsv(self.h,s,255))
            p.fillRect(0,s,256,1,g)

        p.setPen(QColor("black"))
        p.drawRect(0,0,255,255)
        p.end()

    def initialize_hue(self):
        self.hue_pixmap = QPixmap(self.hue_rect.size())
        p = QPainter(self.hue_pixmap)
        for h in range(359):
            p.setPen(QColor.fromHsv(h,255,255))
            p.drawLine(h,self.hue_pixmap.rect().top(),h,self.hue_pixmap.rect().bottom())

        p.setPen(QColor("black"))
        p.drawRect(self.hue_rect)
        p.end()

    def initialize_saturation(self):
        self.saturation_pixmap = QPixmap(self.sat_rect.size())
        p = QPainter(self.saturation_pixmap)
        for s in range(255):
            p.setPen(QColor.fromHsv(self.selected_color.hue(),s,255))
            p.drawLine(s,self.saturation_pixmap.rect().top(),s,self.saturation_pixmap.rect().bottom())

        p.setPen(QColor("black"))
        p.drawRect(self.sat_rect)
        p.end()

    def initialize_value(self):
        self.value_pixmap = QPixmap(self.value_rect.size())
        p = QPainter(self.value_pixmap)
        for v in range(255):
            p.setPen(QColor.fromHsv(self.selected_color.hue(),self.selected_color.saturation(),v))
            p.drawLine(v,self.value_pixmap.rect().top(),v,self.value_pixmap.rect().bottom())
        p.setPen(QColor("black"))
        p.drawRect(self.value_rect)
        p.end()

    def get_pen(self):
        return QPen(self.selected_color)

    def set_color(self, color):
        go = False

        if self.selected_color != color:
            self.h = color.hue()
            self.v = color.value()
            self.s = color.saturation()

            if self.selected_color.hue() != color.hue():
                #self.initialize_spectrum()
                self.initialize_saturation()
                self.initialize_value()

        self.selected_color = color
        self.emit(SIGNAL("colorchange(QColor)"), self.selected_color)
        self.update()
        
    def paintEvent(self, ev):
        pix = QPixmap(self.rect().size())
        
        p = QPainter(pix)

        p.fillRect(self.rect(), QBrush(QColor("white")))

        #p.drawPixmap(0, 0, self.spectrum_pixmap)
        p.drawPixmap(self.hue_rect, self.hue_pixmap)
        p.drawPixmap(self.sat_rect, self.saturation_pixmap)
        p.drawPixmap(self.value_rect, self.value_pixmap)

        p.setOpacity(0.5)
        p.drawRect(self.selected_color.hue() - 2,self.hue_rect.top(),2,16)
        p.drawRect(self.selected_color.saturation() - 2,self.sat_rect.top(),2,16)
        p.drawRect(self.selected_color.value() - 2,self.value_rect.top(),2,16)
        p.setOpacity(1.0)

        p.fillRect(QRect(360,0,self.value_rect.bottom(),self.value_rect.bottom()), QBrush(self.selected_color))
        border = QRect(QPoint(0,0),QSize(self.rect().right(),self.rect().bottom()))
        p.drawRect(border)
        
        p = QPainter(self)
        
        p.drawPixmap(0,0,pix)
        
    def mouseMoveEvent(self, ev):
        """
        if self.dragging_gradient:
            if(self.gradient_rect.contains(ev.pos())):
                self.selected_color = QColor.fromHsv(self.h,ev.pos().y(),ev.pos().x())
                self.s = ev.pos().y()
                self.v = ev.pos().x()

            self.set_color(QColor.fromHsv(self.h,self.s,self.v))

            self.update()
        """

        if self.dragging:
            if self.hue_rect.contains(ev.pos().x(), ev.pos().y()):
                self.h = ev.pos().x()

            if self.sat_rect.contains(ev.pos().x(), ev.pos().y()):
                self.s = ev.pos().x()

            if self.value_rect.contains(ev.pos().x(), ev.pos().y()):
                self.v = ev.pos().x()

            self.set_color(QColor.fromHsv(self.h,self.s,self.v))

            self.update()
                
    def mouseReleaseEvent(self, ev):
        self.dragging_gradient = False
        self.dragging = False
        
    def mousePressEvent(self, ev):
        """
        if(self.gradient_rect.contains(ev.pos())):
            self.dragging_gradient = True
            self.s = ev.pos().y()
            self.v = ev.pos().x()
            """

        if self.hue_rect.contains(ev.pos()):
            self.dragging = True
            self.h = ev.pos().x()

        if self.sat_rect.contains(ev.pos()):
            self.dragging = True
            self.s = ev.pos().x()

        if self.value_rect.contains(ev.pos()):
            self.dragging = True
            self.v = ev.pos().x()

        self.selected_color = QColor.fromHsv(self.h,self.s,self.v)

        self.update()

class ColorSwatch(QWidget):
    def __init__(self, width, height, color, main_swatch = None):
        QWidget.__init__(self, None)
        self.main_swatch = main_swatch
        self.setAcceptDrops(True)
        self.set_size(width, height)
        self.color_picker = ColorPickerDialog()

        self.connect(self.color_picker.color_picker, SIGNAL("colorchange(QColor)"), self.oncolorchange_update)
        self.connect(self.color_picker, SIGNAL("accepted()"), self.dialogfinished)
        self.connect(self.color_picker, SIGNAL("rejected()"), self.dialogfinished)
        self.set_color(color)

    def oncolorchange_update(self, color):
        self.set_color(color)

    def set_color(self, color):
        self.color = color
        self.emit(SIGNAL("colorchange(QColor)"), self.color)
        if self.main_swatch:
            self.main_swatch.set_color(self.color)
        self.update()

    def set_size(self, width, height):
        self.width = width
        self.height = height
        self.setMinimumSize(self.width,self.height)
        self.setMaximumSize(self.width,self.height)
        self.resize(self.width,self.height)
        
    def paintEvent(self, ev):
        p = QPainter(self)
        p.fillRect(self.rect(), QBrush(self.color))
        pen = QPen(QColor(0,0,0))
        p.setPen(pen)
        p.drawRect(0,0,self.rect().width()-1,self.rect().height()-1)

    def mouseReleaseEvent(self, ev):
        self.start_drag = False

    def mousePressEvent(self, ev):
        self.start_drag = True

    def mouseDoubleClickEvent(self, ev):
        self.color_picker.color_picker.set_color(self.color)
        self.color_picker.show()

        if self.color_picker.result() and self.main_swatch:
            self.main_swatch.set_color(self.color)

    def dropEvent(self, ev):
        self.drag_hover = False
        swatch = ev.source()
        self.set_color(swatch.color)
        self.emit(SIGNAL("changed()"))

    def dragEnterEvent(self, ev):
        if ev.source() != self:
            ev.acceptProposedAction()
            self.drag_hover = True
            self.update()

    def dragLeaveEvent(self, ev):
        self.drag_hover = False
        self.update()

    def mouseMoveEvent(self, ev):
        if self.start_drag:
            self.drag = QDrag(self)
            self.payload = QMimeData()
            self.payload.setText(str(self.color.name()))
            self.drag.setMimeData(self.payload)
            self.drag.start()
            self.dragging = True

    def dialogfinished(self):
        if self.color_picker.result():
            self.color = self.color_picker.color_picker.selected_color
        
class ColorGradient(QWidget):
    def __init__(self, width, height, start_swatch, end_swatch):
        QWidget.__init__(self, None)

        self.set_size(width, height)

        self.start_swatch = start_swatch
        self.end_swatch = end_swatch

        self.stepping = 32

        self.selected_color_index = None

        self.colors = []
            
    def set_size(self, width, height):
        self.width = width
        self.height = height
        self.setMinimumSize(self.width,self.height)
        self.setMaximumSize(self.width,self.height)
        self.resize(self.width,self.height)

    def move_left(self):
        if self.selected_color_index != 0:
            self.selected_color_index -= 1
            ev = QEvent(0)
            ev.gradient = self
            ev.color = self.colors[self.selected_color_index]
            self.emit(SIGNAL("colorchange(QEvent)"), ev)
            self.update()

    def move_right(self):
        if self.selected_color_index != self.stepping - 1:
            self.selected_color_index += 1
            ev = QEvent(0)
            ev.gradient = self
            ev.color = self.colors[self.selected_color_index]
            self.emit(SIGNAL("colorchange(QEvent)"), ev)
            self.update()

    def try_select_color(self, color):
        result = False

        for i, c in enumerate(self.colors):
            if color == c:
                self.selected_color_index = i
                self.update()
                result = True
                break

        return result

    def mousePressEvent(self, ev):
        x = ev.pos().x()
        y = ev.pos().y()

        tx = x / (self.rect().width() / self.stepping)
        ty = y / (self.rect().width() / self.stepping)

        self.selected_color_index = tx

        img = self.last_pixmap.toImage()
        pixel = Pixel.getPixel(img, x, y)
        ev.gradient = self
        ev.color = pixel.color
        self.emit(SIGNAL("colorchange(QEvent)"), ev)

        self.update()

    def paintEvent(self, ev):
        down = QPixmap(self.stepping,1)
        p = QPainter(down)
        g = QLinearGradient(0,1,self.stepping,1)
        g.setColorAt(0.0,self.start_swatch.color)
        g.setColorAt(1.0,self.end_swatch.color)
        p.fillRect(0,0,self.stepping,self.height,g)
        p.end()

        up = down.scaled(self.width,self.height)
        q = QPainter(self)
        q.fillRect(self.rect(),QBrush(QColor("white")))
        q.drawPixmap(0,0,up)

        #pen = QPen(QColor(0,0,0))
        #q.setPen(pen)
        #q.drawRect(0,1,self.rect().width()-1,self.rect().height()-2)


        for i in range(self.stepping):
            x = (self.rect().width() / self.stepping) * i
            q.setOpacity(0.1)
            q.drawLine(x,0,x,self.rect().height())

        q.setOpacity(1)

        if self.selected_color_index != None:
            q.drawRect(self.selected_color_index * (self.rect().width()/self.stepping),0,(self.rect().width()/self.stepping) - 1,self.rect().height()-1)
        q.end()

        self.last_pixmap = up

        img = up.toImage()


        # TODO: This should probably be moved to another subroutine.
        self.colors = []

        for i in range(self.stepping):
            x = (self.rect().width() / self.stepping) * i
            pixel = img.pixel(x+1, 0)
            self.colors.append(QColor(pixel))

class ColorGradientBar(QWidget):
    def __init__(self, start_color, end_color, main_swatch = None):
        QWidget.__init__(self, None)

        self.main_swatch = main_swatch

        self.hbox = QHBoxLayout()
        self.start_color = start_color
        self.end_color = end_color
        self.start_swatch = ColorSwatch(10, 10, self.start_color, main_swatch)
        self.end_swatch = ColorSwatch(10, 10, self.end_color, main_swatch)
        self.gradient = ColorGradient(256, 10, self.start_swatch, self.end_swatch)
        self.die_button = QPushButton("-")

        self.hbox.addWidget(self.start_swatch)
        self.hbox.addWidget(self.gradient)
        self.hbox.addWidget(self.end_swatch)
        self.hbox.addWidget(self.die_button)

        self.connect(self.die_button, SIGNAL("clicked()"), self.ondiebutton)
        self.connect(self.gradient, SIGNAL("colorchange(QEvent)"), self.oncolorchange)
        self.connect(self.start_swatch, SIGNAL("colorchange(QColor)"), self.oncolorchange_update)
        self.connect(self.end_swatch, SIGNAL("colorchange(QColor)"), self.oncolorchange_update)

        self.setLayout(self.hbox)

    def ondiebutton(self):
        self.emit(SIGNAL("removeme(QWidget)"), self)

    def oncolorchange(self, event):
        self.emit(SIGNAL("colorchange(QEvent)"), event)

    def oncolorchange_update(self, color):
        self.emit(SIGNAL("swatchcolorchange(QColor)"), color)
        self.update()

class ColorGradientList(QWidget):
    def __init__(self, sprite_editor):
        QWidget.__init__(self, None)

        self.sprite_editor = sprite_editor

        self.vbox = QVBoxLayout()

        self.setLayout(self.vbox)

        self.buttonhbox = QHBoxLayout()

        self.buttons = {}

        self.buttons["add_gradient"] = QPushButton("+")

        for key,button in self.buttons.items():
            self.connect(button, SIGNAL("clicked()"), getattr(self,key))
            self.buttonhbox.addWidget(button)
            self.buttonhbox.setAlignment(button, Qt.AlignLeft)

        self.vbox.addLayout(self.buttonhbox)

        self.swatch = ColorSwatch(50, 50, QColor(128,0,128))

        self.swap = ColorSwap(self.swatch.color, self.sprite_editor)
        self.vbox.addWidget(self.swap)

        self.gradients = []
        self.add_gradient()

        self.buttonhbox.addWidget(self.swatch)

        self.selected_gradient = None

    def left_color(self):
        if self.selected_gradient:
            self.selected_gradient.move_left()
            
    def right_color(self):
        if self.selected_gradient:
            self.selected_gradient.move_right()

    def set_color(self, color):
        self.swatch.set_color(color)
        self.swap.color_a.set_color(color)

    def add_gradient(self):
        bar = ColorGradientBar(QColor("white"), QColor("black"), self.swatch)
        self.gradients.append(bar.gradient)
        self.vbox.addWidget(bar)
        self.connect(bar, SIGNAL("colorchange(QEvent)"), self.oncolorchange)
        self.connect(bar, SIGNAL("swatchcolorchange(QColor)"), self.onswatchcolorchange)
        self.connect(bar, SIGNAL("removeme(QWidget)"), self.remove_bar)

    def remove_bar(self, bar):
        self.vbox.removeWidget(bar)
        self.gradients.remove(bar.gradient)
        bar.deleteLater()

    def oncolorchange(self, ev):
        self.selected_gradient = ev.gradient
        for gradient in self.gradients:
            if id(gradient) != id(ev.gradient):
                gradient.selected_color_index = None
                gradient.update()

        self.emit(SIGNAL("colorchange(QColor)"), ev.color)

        self.swatch.set_color(ev.color)

    def try_select_color(self, color):
        win = None
        for gradient in self.gradients:
            if gradient.try_select_color(color):
                gradient.update()
                win = gradient
                self.selected_gradient = gradient
                break
        if win:
            for gradient in self.gradients:
                if id(gradient) != id(win):
                    gradient.selected_color_index = None
                    gradient.update()

    def onswatchcolorchange(self, color):
        self.emit(SIGNAL("colorchange(QColor)"), color)

        self.try_select_color(color)

        self.swatch.set_color(color)
